package com.lines;

import java.util.List;

import com.lines.Game;
import com.lines.Graphics;
import com.lines.Screen;
import com.lines.Input.TouchEvent;

public class GameScreen extends Screen {
	
	enum GameState {
		Ready,
		Running,
		Paused,
		GameOver
	}
	
	class dir {     // directions
		private int x,y; 
		public dir (int x1, int y1) {
			x = x1; y = y1;
		}
	}
	
	
	static final float TICK_INITIAL = 0.2f;
	static final int FIELD_X = 10; // offset from up-right corner for field
	static final int FIELD_Y = 10; // offset from up-right corner for field 
	static final int M_SIZE = 33;  // size of cell in pixels
	static final int NUM_CEL = 9;  // number of cells in field
	
	GameState state = GameState.Ready;
	int oldScore = 0;
	String score = "0";
	float tickTime = 0;
	int curFrame = 0;
	int selX = -1, selY = -1;
	int field[][];
	dir Dir8[];
	dir Dir4[];
	int path[], pathLen;
	
	public GameScreen(Game game) {
		super(game);
		field = new int [NUM_CEL][NUM_CEL];
		Dir8 = new dir[8];
		Dir4 = new dir[4];
		//java.util.Arrays.fill(Dir8, new dir());
		Dir8[0] = new dir(1, 0);
		Dir8[1] = new dir(1, -1);
		Dir8[2] = new dir(0, -1);
		Dir8[3] = new dir(-1, -1);
		Dir8[4] = new dir(-1, 0);
		Dir8[5] = new dir(-1, 1);
		Dir8[6] = new dir(0, 1);
		Dir8[7] = new dir(1, 1);
		
		Dir4[0] = new dir(1, 0);
		Dir4[1] = new dir(0, -1);
		Dir4[2] = new dir(-1, 0);
		Dir4[3] = new dir(0, 1);
		
		path = new int [NUM_CEL*NUM_CEL + 1 ]; //storage for path for the ball 
		
		createNew();
	}
		
	@Override
	public void update(float deltaTime) {
		List<TouchEvent> touchEvents = game.getInput().getTouchEvents();
		game.getInput().getKeyEvents();
		
		int len = touchEvents.size();
		
		if(state == GameState.Ready) {
			for(int i = 0; i < len; i++) {
				TouchEvent event = touchEvents.get(i);
				if(event.type == TouchEvent.TOUCH_DOWN) {
					if(event.x > 0 && event.x < 64 &&
							event.y > 416 && event.y < 480) {
						Settings.soundEnabled = !Settings.soundEnabled;
						if(Settings.soundEnabled)
							Assets.click.play(1);
					}
					if(event.x > 80 && event.x < 144 &&
							event.y > 416 && event.y < 480) {
						if(Settings.soundEnabled)
							Assets.click.play(1);
						game.setScreen(new MainMenuScreen(game));
						return;
					}
					if(event.x > FIELD_X && event.x < FIELD_X + NUM_CEL*M_SIZE 
							&& event.y > FIELD_Y && event.y < FIELD_Y + NUM_CEL*M_SIZE) {
						int newX = (event.x - FIELD_X) / M_SIZE, newY = (event.y - FIELD_Y) / M_SIZE;
					
						if (selX >= 0 && field[newX][newY] == 0) { //ball was chosen and we have new place for it
							if (createPath(newX, newY)) {
								state = GameState.Running;
								return;
							}
						} else if ((selX >= 0 && field[newX][newY] > 0) || selX < 0) { //we selected new ball or just first ball
							selX = newX;
							selY = newY;
						}
						
					} else {  // out of field - no balls
						selY = -1;
						selX = -1;
					}
					
				}
			}
		}
		
		if (state == GameState.Running && curFrame % 2 == 0) { //move the ball every second tick
			int tmpX = selX, tmpY = selY;
			selX -= Dir4[path[pathLen]].x; //we go in reverse order
			selY -= Dir4[path[pathLen]].y;
			field[selX][selY] = field[tmpX][tmpY];
			field[tmpX][tmpY] = 0;
			if (pathLen > 0 ) pathLen--;
			else {
				if (!createNew()) {
					Settings.addScore(oldScore);
					Settings.save(game.getFileIO());
					state = GameState.GameOver;
					return;
				}
				if (checkLines()) {
					score = "" + oldScore;
					selX = -1;
					selY = -1;
				}
				state = GameState.Ready;
			}
		}
		
		if (state == GameState.GameOver) 
			for(int i = 0; i < len; i++) {
				TouchEvent event = touchEvents.get(i);
				if(event.type == TouchEvent.TOUCH_DOWN &&
						event.x > 64 && event.x < 256 &&
						event.y > 350 && event.y < 410) {
					if(Settings.soundEnabled)
						Assets.click.play(1);
					game.setScreen(new MainMenuScreen(game));
					return;
				}
			}
	}
	
	private boolean createPath (int destX, int destY) {
		boolean contin;
		int tmp_field[][] = new int [NUM_CEL][NUM_CEL];
		tmp_field[selX][selY] = 1; //initial wave
		do {
			contin = false;
			for (int y=0; y<NUM_CEL; y++)
				for(int x=0; x<NUM_CEL; x++) {
					if (tmp_field[x][y] > 0) {
						int wave_lev = tmp_field[x][y]; // current level of wave
						for (int i = 0; i < 4; i++) { // search around
							if (x + Dir4[i].x >= 0 && x + Dir4[i].x < NUM_CEL &&
									y + Dir4[i].y >= 0 && y + Dir4[i].y < NUM_CEL &&
									tmp_field[x + Dir4[i].x][y + Dir4[i].y] == 0 &&
									field[x + Dir4[i].x][y + Dir4[i].y] == 0) { //clear place?
								tmp_field[x + Dir4[i].x][y + Dir4[i].y] = wave_lev + 1; //increase level
								contin = true; // we still can move, have place for it
							}
							if (x + Dir4[i].x == destX && y + Dir4[i].y == destY) { //found finish point, we have the path
								int tx = destX, ty = destY, c = 0; //create the path from finish to start
								while (tx != selX || ty != selY) {
									for (int j = 0; j < 4; j++) {
										if (tx + Dir4[j].x >= 0 && tx + Dir4[j].x < NUM_CEL &&
												ty + Dir4[j].y >= 0 && ty + Dir4[j].y < NUM_CEL &&
												tmp_field[tx + Dir4[j].x][ty + Dir4[j].y] == wave_lev) {
											tx += Dir4[j].x; //next point
											ty += Dir4[j].y;
											path[c] = j;
											c++;
											wave_lev--;
											break;
										}
									}
								}
								pathLen = c - 1;
								return true;
							}
						}
					} 
				}
		} while (contin); 
		return false;
	}
	
	/*
	 * Create 3 new balls in random positions and random color
	 * if there are no 3 empty cell in field then return false
	 */
	private boolean createNew () {
		//here we will use path[] as temporary storage
		int c = 0; //counter of free cells
		for (int y=0; y<NUM_CEL; y++) //fill that storage
			for(int x=0; x<NUM_CEL; x++) {
				if (field[x][y] == 0) path[c++] = y * NUM_CEL + x; 
			}
		if (c < 3) return false; // do not have free cells;
		for (int i = 0; i < 3; i++) {
			int n = (int) (Math.random() * c); // get random cell from our storage
			int y = (int) (path[n] / NUM_CEL); // get coord's from number
			int x = (int) (path[n] % NUM_CEL);
			field[x][y] = (int) (Math.random() * 5) + 1; // get random color
			for (x = n; x < c; x++) path[x] = path[x+1]; //remove used cell
			c--;
		}
		if (c == 0) return false; // all are filled;
		return true;
	}
	
	/*
	 * Check if there is line of 5 or more balls of one color in line,
	 * checks in 8 directions (including diagonals), removes found balls,
	 * increases "oldScore" result.
	 * Returns true in case of success (line was found), false if there is 
	 * no lines.  
	 */
	private boolean checkLines() {
		boolean res = false;
		for (int y=0; y<NUM_CEL; y++) //go through the all squares
			for(int x=0; x<NUM_CEL; x++) {
				if (field[x][y] != 0){
					int c = field[x][y];
					for (int j = 0; j < 8; j++) { //check all directions around our ball
						int count = 1;
						while (x + Dir8[j].x * count >= 0 && x + Dir8[j].x * count < NUM_CEL &&
							y + Dir8[j].y * count >= 0 && y + Dir8[j].y * count < NUM_CEL &&
							field[x + Dir8[j].x * count][y + Dir8[j].y * count] == c) count++; //count them
						count--;
						if (count > 3) { //do we have the line of 5 or more balls (counts from zero)
							oldScore += count + 1;
							while (count >= 0) {
								field[x + Dir8[j].x * count][y + Dir8[j].y * count] = 0; //remove them
								count--;
							}
							res = true;
						}
					}
					
				}
					
			}
		return res;
	}
	
		
	@Override
	public void present(float deltaTime) {
		Graphics g = game.getGraphics();
		g.drawPixmap(Assets.background, 0, 0);
		for (int y=0; y<NUM_CEL; y++)
			for(int x=0; x<NUM_CEL; x++) {
				g.drawPixmap(Assets.board, FIELD_X+x*M_SIZE, FIELD_Y+y*M_SIZE);
				if (field[x][y] > 0)
					if (x == selX && y == selY)
						g.drawPixmap(Assets.balls, FIELD_X+x*M_SIZE, FIELD_Y+y*M_SIZE, 
								curFrame*M_SIZE, (field[x][y]-1)*M_SIZE, M_SIZE, M_SIZE);
					else
						g.drawPixmap(Assets.balls, FIELD_X+x*M_SIZE, FIELD_Y+y*M_SIZE, 
								0, (field[x][y]-1)*M_SIZE, M_SIZE, M_SIZE);
			}

		if(state == GameState.GameOver) {
			g.drawPixmap(Assets.gameOver, 64, 350, 0, curFrame*60, 192, 60);
		}
		
		tickTime += deltaTime;
		while (tickTime > TICK_INITIAL) {
			tickTime -= TICK_INITIAL;
			if(selX >= 0) {
				curFrame++;
				if (curFrame == 4 && Settings.soundEnabled)
					Assets.tap.play(1);
				if(curFrame > 4) curFrame = 0;
			} else 
				curFrame = 0;
		}
		

		if(Settings.soundEnabled)
			g.drawPixmap(Assets.buttons, 0, 416, 0, 0, 64, 64);
		else
			g.drawPixmap(Assets.buttons, 0, 416, 64, 0, 64, 64);
		g.drawPixmap(Assets.buttons, 80, 416, 0, 64, 64, 64); //close button
		drawText(g, score, g.getWidth() / 2 - score.length()*20 / 2, g.getHeight() - 42);
	}
		

	
	public void drawText(Graphics g, String line, int x, int y) {
		int len = line.length();
		for (int i = 0; i < len; i++) {
			char character = line.charAt(i);
			if (character == ' ') {
				x += 20;
				continue;
			}
			int srcX = 0;
			int srcWidth = 0;
			if (character == '.') {
				srcX = 200;
				srcWidth = 10;
			} else {
				srcX = (character - '0') * 20;
				srcWidth = 20;
			}
			g.drawPixmap(Assets.numbers, x, y, srcX, 0, srcWidth, 32);
			x += srcWidth;
		}
	}
			
	@Override
	public void pause() {
	}
	
	@Override
	public void resume() {
	}
	
	@Override
	public void dispose() {
	}
	
}

			
